﻿#pragma warning disable 0420

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace LibSnesDotNet.Threading
{
	class WaitableBool : IWaitableBool
	{
		public static implicit operator bool(WaitableBool value)
		{
			return value._value != 0;
		}

		volatile int _value;
		ManualResetEvent _whileHandle;
		ManualResetEvent _untilHandle;

		public WaitableBool(bool value)
		{
			_whileHandle = new ManualResetEvent(false);
			_untilHandle = new ManualResetEvent(false);

			if (value)
			{
				_value = 1;
				_whileHandle.Reset();
				_untilHandle.Set();
			}
			else
			{
				_value = 0;
				_whileHandle.Set();
				_untilHandle.Reset();
			}
		}

		public bool Value { get { return _value > 0; } set { TryAcquire(value); } }

		public void Acquire(bool value)
		{
			while (!TryAcquire(value))
			{
				WaitFor(!value);
			}
		}

		public bool TryAcquire(bool value)
		{
			if (value && (_value <= 0))
			{
				while (_value == -1) { }

				if (Interlocked.CompareExchange(ref _value, 2, 0) == 0)
				{
					_whileHandle.Reset();
					_untilHandle.Set();
					_value = 1;
					return true;
				}
			}
			else if (!value && (_value > 0))
			{
				while (_value == 2) { }

				if (Interlocked.CompareExchange(ref _value, -1, 1) == 1)
				{
					_untilHandle.Reset();
					_whileHandle.Set();
					_value = 0;
					return true;
				}
			}

			return false;
		}

		public void WaitUntil()
		{
			_untilHandle.WaitOne();
		}

		public void WaitWhile()
		{
			_whileHandle.WaitOne();
		}

		public void WaitFor(bool value)
		{
			if (value)
			{
				_untilHandle.WaitOne();
			}
			else
			{
				_whileHandle.WaitOne();
			}
		}
	}
}
